Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
webpack-merge
Advanced tools
The webpack-merge package provides a utility to merge multiple webpack configurations. It is useful for composing different webpack configurations for development, production, or any other purpose. It simplifies the process of combining configurations by smartly merging loaders, plugins, and other specific structures within the webpack configuration objects.
Merging basic configurations
This feature allows for the merging of two basic webpack configurations into one. It is useful for separating common configuration from environment-specific configurations.
const { merge } = require('webpack-merge');
const commonConfig = { entry: './src/app.js' };
const productionConfig = { mode: 'production' };
const mergedConfig = merge(commonConfig, productionConfig);
Merging with rules
This demonstrates merging configurations that include module rules. webpack-merge smartly combines the loaders, allowing for a nuanced configuration that can differ between development and production environments.
const { merge } = require('webpack-merge');
const commonConfig = { module: { rules: [ { test: /\.css$/, use: ['style-loader'] } ] } };
const productionConfig = { module: { rules: [ { test: /\.css$/, use: ['css-loader'] } ] } };
const mergedConfig = merge(commonConfig, productionConfig);
Customizing merge behavior
This feature allows for customizing the merge behavior, such as prepending or appending elements in arrays instead of the default merging strategy. It's particularly useful for fine-tuning the order of plugins or loaders.
const { mergeWithCustomize, customizeArray } = require('webpack-merge');
const commonConfig = { plugins: ['CommonPlugin'] };
const productionConfig = { plugins: ['ProductionPlugin'] };
const mergedConfig = mergeWithCustomize({ customizeArray: customizeArray({ 'plugins': 'prepend' }) })(commonConfig, productionConfig);
deepmerge is a library that can deeply merge multiple objects into one. While it's not specific to webpack configurations, it can be used for a similar purpose. However, webpack-merge offers webpack-specific merging capabilities that deepmerge does not, such as smart merging of loaders and plugins.
lodash.merge is a method from the Lodash library that provides a deep merge of objects. Similar to deepmerge, it can be used to merge webpack configurations but lacks the webpack-specific intelligence of webpack-merge, potentially requiring more manual configuration for complex webpack setups.
webpack-merge provides a merge
function that concatenates arrays and merges objects creating a new object. If functions are encountered, it will execute them, run the results through the algorithm, and then wrap the returned values within a function again.
This behavior is particularly useful in configuring webpack although it has uses beyond it. Whenever you need to merge configuration objects, webpack-merge can come in handy.
merge(...configuration | [...configuration])
merge
is the core, and the most important idea, of the API. Often this is all you need unless you want further customization.
const { merge } = require('webpack-merge');
// Default API
const output = merge(object1, object2, object3, ...);
// You can pass an array of objects directly.
// This works with all available functions.
const output = merge([object1, object2, object3]);
// Keys matching to the right take precedence:
const output = merge(
{ fruit: "apple", color: "red" },
{ fruit: "strawberries" }
);
console.log(output);
// { color: "red", fruit: "strawberries"}
Note that Promise
s are not supported! If you want to return a configuration wrapped within a Promise
, merge
inside one. Example: Promise.resolve(merge({ ... }, { ... }))
.
The same goes for configuration level functions as in the example below:
webpack.config.js
const commonConfig = { ... };
const productionConfig = { ... };
const developmentConfig = { ... };
module.exports = (env, args) => {
switch(args.mode) {
case 'development':
return merge(commonConfig, developmentConfig);
case 'production':
return merge(commonConfig, productionConfig);
default:
throw new Error('No matching configuration was found!');
}
}
You can choose the configuration you want by using webpack --mode development
assuming you are using webpack-cli.
mergeWithCustomize({ customizeArray, customizeObject })(...configuration | [...configuration])
In case you need more flexibility, merge
behavior can be customized per field as below:
const { mergeWithCustomize } = require('webpack-merge');
const output = mergeWithCustomize(
{
customizeArray(a, b, key) {
if (key === 'extensions') {
return _.uniq([...a, ...b]);
}
// Fall back to default merging
return undefined;
},
customizeObject(a, b, key) {
if (key === 'module') {
// Custom merging
return _.merge({}, a, b);
}
// Fall back to default merging
return undefined;
}
}
)(object1, object2, object3, ...);
For example, if the previous code was invoked with only object1
and object2
with object1
as:
{
foo1: ['object1'],
foo2: ['object1'],
bar1: { object1: {} },
bar2: { object1: {} },
}
and object2
as:
{
foo1: ['object2'],
foo2: ['object2'],
bar1: { object2: {} },
bar2: { object2: {} },
}
then customizeArray
will be invoked for each property of Array
type, i.e:
customizeArray(["object1"], ["object2"], "foo1");
customizeArray(["object1"], ["object2"], "foo2");
and customizeObject
will be invoked for each property of Object
type, i.e:
customizeObject({ object1: {} }, { object2: {} }, bar1);
customizeObject({ object1: {} }, { object2: {} }, bar2);
customizeArray
and customizeObject
customizeArray
and customizeObject
provide small strategies to for mergeWithCustomize
. They support append
, prepend
, replace
, and wildcards for field names.
const { mergeWithCustomize, customizeArray, customizeObject } = require('webpack-merge');
const output = mergeWithCustomize({
customizeArray: customizeArray({
'entry.*': 'prepend'
}),
customizeObject: customizeObject({
entry: 'prepend'
})
})(object1, object2, object3, ...);
unique(<field>, <fields>, field => field)
unique
is a strategy used for forcing uniqueness within configuration. It's most useful with plugins when you want to make sure there's only one in place.
The first <field>
is the config property to look through for duplicates.
<fields>
represents the values that should be unique when you run the field => field function on each duplicate.
When the order of elements of the <field>
in the first configuration differs from the order in the second configuration, the latter is preserved.
const { mergeWithCustomize, unique } = require("webpack-merge");
const output = mergeWithCustomize({
customizeArray: unique(
"plugins",
["HotModuleReplacementPlugin"],
(plugin) => plugin.constructor && plugin.constructor.name
),
})(
{
plugins: [new webpack.HotModuleReplacementPlugin()],
},
{
plugins: [new webpack.HotModuleReplacementPlugin()],
}
);
// Output contains only single HotModuleReplacementPlugin now and it's
// going to be the last plugin instance.
mergeWithRules
To support advanced merging needs (i.e. merging within loaders), mergeWithRules
includes additional syntax that allows you to match fields and apply strategies to match. Consider the full example below:
const a = {
module: {
rules: [
{
test: /\.css$/,
use: [{ loader: "style-loader" }, { loader: "sass-loader" }],
},
],
},
};
const b = {
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: "style-loader",
options: {
modules: true,
},
},
],
},
],
},
};
const result = {
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: "style-loader",
options: {
modules: true,
},
},
{ loader: "sass-loader" },
],
},
],
},
};
assert.deepStrictEqual(
mergeWithRules({
module: {
rules: {
test: "match",
use: {
loader: "match",
options: "replace",
},
},
},
})(a, b),
result
);
The way it works is that you should annotate fields to match using match
(or CustomizeRule.Match
if you are using TypeScript) matching your configuration structure and then use specific strategies to define how particular fields should be transformed. If a match doesn't exist above a rule, then it will apply the rule automatically.
Supported annotations:
match
(CustomizeRule.Match
) - Optional matcher that scopes merging behavior to a specific part based on similarity (think DOM or jQuery selectors)append
(CustomizeRule.Append
) - Appends itemsprepend
(CustomizeRule.Prepend
) - Prepends itemsreplace
(CustomizeRule.Replace
) - Replaces itemsmerge
(CustomizeRule.Merge
) - Merges objects (shallow merge)webpack-merge supports TypeScript out of the box. You should pass Configuration
type from webpack to it as follows:
import { Configuration } from "webpack";
import { merge } from "webpack-merge";
const config = merge<Configuration>({...}, {...});
...
nvm use
npm i
npm run build -- --watch
in one terminalnpm t -- --watch
in another oneBefore contributing, please open an issue where to discuss.
Check out SurviveJS - Webpack 5 to dig deeper into webpack. The free book uses webpack-merge extensively and shows you how to compose your configuration to keep it maintainable.
I am also available as a consultant in case you require specific assistance. I can contribute particularly in terms of improving maintainability of the setup while speeding it up and pointing out better practices. In addition to improving developer productivity, the work has impact on the end users of the product in terms of reduced application size and loading times.
This project exists thanks to all the people who contribute. [Contribute].
Become a financial contributor and help us sustain our community. [Contribute]
Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]
webpack-merge is available under MIT. See LICENSE for more details.
FAQs
Variant of merge that's useful for webpack configuration
The npm package webpack-merge receives a total of 7,363,950 weekly downloads. As such, webpack-merge popularity was classified as popular.
We found that webpack-merge demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.